/*
 * Copyright 2019 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eclipse.emf.ecore.xmi.util;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;

public class ECollections {

    // Suppress default constructor for noninstantiability.
    private ECollections() {
        super();
    }

    /**
     * Moves the object to the new position, if is in the list.
     * @param list
     * @param newPosition the position of the object after the move.
     * @param object the object to move.
     */
    public static <T> void move(List<T> list, int newPosition, T object) {
        if (list instanceof org.eclipse.emf.common.util.EList<?>) {
            ((org.eclipse.emf.common.util.EList<T>) list).move(newPosition, object);
        } else {
            list.remove(object);
            list.add(newPosition, object);
        }
    }

    /**
     * Moves the object from the old position to the new position.
     * @param list
     * @param targetIndex the position of the object after the move.
     * @param sourceIndex the position of the object before the move.
     * @return the moved object
     */
    public static <T> T move(List<T> list, int targetIndex, int sourceIndex) {
        if (list instanceof org.eclipse.emf.common.util.EList<?>) {
            return ((org.eclipse.emf.common.util.EList<T>) list).move(targetIndex, sourceIndex);
        } else {
            T object = list.remove(sourceIndex);
            list.add(targetIndex, object);
            return object;
        }
    }

    /**
     * Reverses the order of the elements in the specified EList.
     */
    public static void reverse(org.eclipse.emf.common.util.EList<?> list) {
        int last = list.size() - 1;
        for (int i = 0; i < last; i++) {
            list.move(i, last);
        }
    }

    /**
     * Searches for the first occurrence of the given argument in list starting from
     * a specified index.  The equality is tested using the operator <tt>==<tt> and
     * the <tt>equals</tt> method.
     * @param list
     * @param o an object (can be null)
     * @param fromIndex
     * @return the index of the first occurrence of the argument in this
     * list (where index>=fromIndex); returns <tt>-1</tt> if the
     * object is not found.
     * @since 2.1.0
     */
    public static int indexOf(List<?> list, Object o, int fromIndex) {
        if (fromIndex < 0) {
            fromIndex = 0;
        }

        int size = list.size();
        for (int i = fromIndex; i < size; i++) {
            Object element = list.get(i);
            if (o == null) {
                if (element == null) {
                    return i;
                }
            } else if (o == element || o.equals(element)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Sorts the specified list.  Use this method instead of
     * {@link Collections#sort(List)} to
     * avoid errors when sorting unique lists.
     * @since 2.1.0
     */
    public static void sort(org.eclipse.emf.common.util.EList<?> list) {
        Object[] listAsArray = list.toArray();
        Arrays.sort(listAsArray);
        for (int i = 0; i < listAsArray.length; i++) {
            int oldIndex = indexOf(list, listAsArray[i], i);
            if (i != oldIndex) {
                list.move(i, oldIndex);
            }
        }
    }

    /**
     * Sorts the specified list based on the order defined by the
     * specified comparator.  Use this method instead of
     * {@link Collections#sort(List, Comparator)} to
     * avoid errors when sorting unique lists.
     * @since 2.1.0
     */
    public static <T> void sort(org.eclipse.emf.common.util.EList<T> list, Comparator<? super T> comparator) {
        Object[] listAsArray = list.toArray();
        @SuppressWarnings("unchecked")
        Comparator<Object> objectComparator = (Comparator<Object>) comparator;
        Arrays.sort(listAsArray, objectComparator);
        for (int i = 0; i < listAsArray.length; i++) {
            int oldIndex = indexOf(list, listAsArray[i], i);
            if (i != oldIndex) {
                list.move(i, oldIndex);
            }
        }
    }

    /**
     * Sets the <code>eList</code>'s contents and order to be exactly that of the <code>prototype</code> list.
     * This implementation minimizes the number of notifications the operation will produce.
     * Objects already in the list will be moved, missing objects will be added, and extra objects will be removed.
     * If <code>eList</code>'s contents and order are already exactly that of the <code>prototype</code> list,
     * no change will be made.
     * @param eList the list to set.
     * @param prototypeList the list representing the desired content and order.
     */
    public static <T> void setEList(org.eclipse.emf.common.util.EList<T> eList, List<? extends T> prototypeList) {
        int index = 0;
        for (T prototypeObject : prototypeList) {
            if (eList.size() <= index) {
                eList.add(prototypeObject);
            } else {
                boolean done;
                do {
                    done = true;
                    Object targetObject = eList.get(index);
                    if (targetObject == null ? prototypeObject != null : !targetObject.equals(prototypeObject)) {
                        int position = indexOf(eList, prototypeObject, index);
                        if (position != -1) {
                            int targetIndex = indexOf(prototypeList, targetObject, index);
                            if (targetIndex == -1) {
                                eList.remove(index);
                                done = false;
                            } else if (targetIndex > position) {
                                if (eList.size() <= targetIndex) {
                                    targetIndex = eList.size() - 1;
                                }
                                eList.move(targetIndex, index);

                                done = false;
                            } else {
                                eList.move(index, position);
                            }
                        } else {
                            eList.add(index, prototypeObject);
                        }
                    }
                }
                while (!done);
            }
            ++index;
        }
        for (int i = eList.size(); i > index; ) {
            eList.remove(--i);
        }
    }

    /**
     * Returns an unmodifiable view of the list.
     * @return an unmodifiable view of the list.
     */
    public static <T> org.eclipse.emf.common.util.EList<T> unmodifiableEList(org.eclipse.emf.common.util.EList<? extends T> list) {
        return new UnmodifiableEList<T>(list);
    }

    /**
     * Returns an unmodifiable view of the map.
     * @return an unmodifiable view of the map.
     *//*
  public static <K, V> EMap<K, V> unmodifiableEMap(EMap<? extends K, ? extends V> map)
  {
    return new UnmodifiableEMap<K, V>(map);
  }*/

    /**
     * An unmodifiable empty list with an efficient reusable iterator.
     */
    public static final org.eclipse.emf.common.util.EList<?> EMPTY_ELIST = new EmptyUnmodifiableEList();

    /**
     * Returns an empty unmodifiable list.
     * @return an empty unmodifiable list.
     */
    @SuppressWarnings("unchecked")
    public static <T> org.eclipse.emf.common.util.EList<T> emptyEList() {
        return (org.eclipse.emf.common.util.EList<T>) EMPTY_ELIST;
    }

    /**
     * An unmodifiable empty map with an efficient reusable iterator.
     *//*
  public static final EMap<?, ?> EMPTY_EMAP = new EmptyUnmodifiableEMap();

  */

    /**
     * Returns an empty unmodifiable map.
     * @return an empty unmodifiable map.
     *//*
  @SuppressWarnings("unchecked")
  public static <K, V> EMap<K, V> emptyEMap()
  {
    return (EMap<K, V>)EMPTY_EMAP;
  }*/

    private static class UnmodifiableEList<E> implements org.eclipse.emf.common.util.EList<E> {

        protected List<? extends E> list;

        public UnmodifiableEList(List<? extends E> list) {
            this.list = list;
        }

        public int size() {
            return list.size();
        }

        public boolean isEmpty() {
            return list.isEmpty();
        }

        public boolean contains(Object o) {
            return list.contains(o);
        }

        public Object[] toArray() {
            return list.toArray();
        }

        public <T> T[] toArray(T[] a) {
            return list.toArray(a);
        }

        @Override
        public String toString() {
            return list.toString();
        }

        public Iterator<E> iterator() {
            return
                    new Iterator<E>() {
                        Iterator<? extends E> i = list.iterator();

                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        public E next() {
                            return i.next();
                        }

                        public void remove() {
                            throw new UnsupportedOperationException();
                        }
                    };
        }

        public boolean add(E o) {
            throw new UnsupportedOperationException();
        }

        public boolean remove(Object o) {
            throw new UnsupportedOperationException();
        }

        public boolean containsAll(Collection<?> coll) {
            return list.containsAll(coll);
        }

        public boolean addAll(Collection<? extends E> coll) {
            throw new UnsupportedOperationException();
        }

        public boolean removeAll(Collection<?> coll) {
            throw new UnsupportedOperationException();
        }

        public boolean retainAll(Collection<?> coll) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean equals(Object o) {
            return list.equals(o);
        }

        @Override
        public int hashCode() {
            return list.hashCode();
        }

        public E get(int index) {
            return list.get(index);
        }

        public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }

        public void add(int index, Object element) {
            throw new UnsupportedOperationException();
        }

        public E remove(int index) {
            throw new UnsupportedOperationException();
        }

        public int indexOf(Object o) {
            return list.indexOf(o);
        }

        public int lastIndexOf(Object o) {
            return list.lastIndexOf(o);
        }

        public boolean addAll(int index, Collection<? extends E> collection) {
            throw new UnsupportedOperationException();
        }

        public ListIterator<E> listIterator() {
            return listIterator(0);
        }

        public ListIterator<E> listIterator(final int index) {
            return
                    new ListIterator<E>() {
                        ListIterator<? extends E> i = list.listIterator(index);

                        public boolean hasNext() {
                            return i.hasNext();
                        }

                        public E next() {
                            return i.next();
                        }

                        public boolean hasPrevious() {
                            return i.hasPrevious();
                        }

                        public E previous() {
                            return i.previous();
                        }

                        public int nextIndex() {
                            return i.nextIndex();
                        }

                        public int previousIndex() {
                            return i.previousIndex();
                        }

                        public void remove() {
                            throw new UnsupportedOperationException();
                        }

                        public void set(E o) {
                            throw new UnsupportedOperationException();
                        }

                        public void add(E o) {
                            throw new UnsupportedOperationException();
                        }
                    };
        }

        public List<E> subList(int fromIndex, int toIndex) {
            return new UnmodifiableEList<E>(new BasicEList<E>(list.subList(fromIndex, toIndex)));
        }

        public void move(int newPosition, E o) {
            throw new UnsupportedOperationException();
        }

        public E move(int newPosition, int oldPosition) {
            throw new UnsupportedOperationException();
        }
    }

  /*private static class UnmodifiableEMap<K, V> extends UnmodifiableEList<Map.Entry<K, V>> implements EMap<K, V>
  {
    protected EMap<? extends K, ? extends V> eMap;

    @SuppressWarnings("unchecked")
    public UnmodifiableEMap(EMap<? extends K, ? extends V> eMap)
    {
      super((EMap<K, V>)eMap);
      this.eMap = eMap;
    }

    public boolean containsKey(Object key)
    {
      return eMap.containsKey(key);
    }

    public boolean containsValue(Object value)
    {
      return eMap.containsValue(value);
    }

    @SuppressWarnings("unchecked")
    public Set<Map.Entry<K, V>> entrySet()
    {
      return Collections.unmodifiableSet((Set<Map.Entry<K, V>>)(Set<?>)eMap.entrySet());
    }

    public V get(Object key)
    {
      return eMap.get(key);
    }

    public int indexOfKey(Object key)
    {
      return eMap.indexOf(key);
    }

    public Set<K> keySet()
    {
      return Collections.unmodifiableSet(eMap.keySet());
    }

    public Map<K, V> map()
    {
      return Collections.unmodifiableMap(eMap.map());
    }

    public Collection<V> values()
    {
      return Collections.unmodifiableCollection(eMap.values());
    }

    public V put(K key, V value)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(EMap<? extends K, ? extends V> map)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(Map<? extends K, ? extends V> map)
    {
      throw new UnsupportedOperationException();
    }

    public V removeKey(Object key)
    {
      throw new UnsupportedOperationException();
    }
  }*/

    private static class BasicEmptyUnmodifiableEList<E> {

        public int size() {
            return 0;
        }

        public boolean isEmpty() {
            return true;
        }

        @Override
        public boolean equals(Object o) {
            return Collections.EMPTY_LIST.equals(o);
        }

        @Override
        public int hashCode() {
            return Collections.EMPTY_LIST.hashCode();
        }

        public E get(int index) {
            Collections.EMPTY_LIST.get(index);
            return null;
        }

        public boolean contains(Object o) {
            return false;
        }

        public int indexOf(Object o) {
            return -1;
        }

        public int lastIndexOf(Object o) {
            return -1;
        }

        ListIterator<E> listIterator =
                new ListIterator<E>() {
                    public boolean hasNext() {
                        return false;
                    }

                    public E next() {
                        throw new NoSuchElementException();
                    }

                    public boolean hasPrevious() {
                        return false;
                    }

                    public E previous() {
                        throw new NoSuchElementException();
                    }

                    public int nextIndex() {
                        return 0;
                    }

                    public int previousIndex() {
                        return -1;
                    }

                    public void remove() {
                        throw new UnsupportedOperationException();
                    }

                    public void set(E o) {
                        throw new UnsupportedOperationException();
                    }

                    public void add(E o) {
                        throw new UnsupportedOperationException();
                    }
                };

        public Iterator<E> iterator() {
            return listIterator;
        }

        public ListIterator<E> listIterator() {
            return listIterator;
        }

        public ListIterator<E> listIterator(int index) {
            return listIterator;
        }

        public List<E> subList(int fromIndex, int toIndex) {
            return Collections.<E>emptyList().subList(fromIndex, toIndex);
        }

        public Object[] toArray() {
            return Collections.EMPTY_LIST.toArray();
        }

        public <T> T[] toArray(T[] a) {
            return Collections.<T>emptyList().toArray(a);
        }

        @Override
        public String toString() {
            return Collections.EMPTY_LIST.toString();
        }

        public boolean add(E o) {
            throw new UnsupportedOperationException();
        }

        public boolean remove(Object o) {
            throw new UnsupportedOperationException();
        }

        public boolean containsAll(Collection<?> coll) {
            return false;
        }

        public boolean addAll(Collection<? extends E> coll) {
            throw new UnsupportedOperationException();
        }

        public boolean removeAll(Collection<?> coll) {
            throw new UnsupportedOperationException();
        }

        public boolean retainAll(Collection<?> coll) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            throw new UnsupportedOperationException();
        }

        public E set(int index, E element) {
            throw new UnsupportedOperationException();
        }

        public void add(int index, E element) {
            throw new UnsupportedOperationException();
        }

        public E remove(int index) {
            throw new UnsupportedOperationException();
        }

        public boolean addAll(int index, Collection<? extends E> collection) {
            throw new UnsupportedOperationException();
        }

        public void move(int newPosition, E o) {
            throw new UnsupportedOperationException();
        }

        public E move(int newPosition, int oldPosition) {
            throw new UnsupportedOperationException();
        }
    }

    private static class EmptyUnmodifiableEList extends BasicEmptyUnmodifiableEList<Object> implements org.eclipse.emf.common.util.EList<Object> {

        private EmptyUnmodifiableEList() {
            super();
        }
    }

  /*private static class EmptyUnmodifiableEMap extends BasicEmptyUnmodifiableEList<Map.Entry<Object, Object>> implements EMap<Object, Object>
  {
    public boolean containsKey(Object key)
    {
      return false;
    }

    public boolean containsValue(Object value)
    {
      return false;
    }

    public Set<Map.Entry<Object, Object>> entrySet()
    {
      return Collections.emptySet();
    }

    public Object get(Object key)
    {
      return null;
    }

    public int indexOfKey(Object key)
    {
      return -1;
    }

    public Set<Object> keySet()
    {
      return Collections.emptySet();
    }

    public Map<Object, Object> map()
    {
      return Collections.emptyMap();
    }

    public Collection<Object> values()
    {
      return Collections.emptyList();
    }

    public Object put(Object key, Object value)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(EMap<? extends Object, ? extends Object> map)
    {
      throw new UnsupportedOperationException();
    }

    public void putAll(Map<? extends Object, ? extends Object> map)
    {
      throw new UnsupportedOperationException();
    }

    public Object removeKey(Object key)
    {
      throw new UnsupportedOperationException();
    }
  }*/

    /**
     * Returns an immutable list containing just the one object.
     * @return an immutable list containing just the one object.
     * @since 2.7
     */
    public static <T> org.eclipse.emf.common.util.EList<T> singletonEList(T o) {
        return new UnmodifiableEList<T>(Collections.singletonList(o));
    }

    /**
     * Returns an immutable map containing just the one key/value mapping.
     * @return an immutable map containing just the one key/value mapping.
     * @since 2.7
     *//*
  public static <K, V> EMap<K, V> singletonEMap(K key, V value)
  {
    BasicEMap<K, V> result = new BasicEMap<K, V>(1);
    result.put(key, value);
    return new UnmodifiableEMap<K, V>(result);
  }

  *//**
     * Returns a mutable list containing the elements of the given iterator.
     * @return a mutable list containing the same elements as the given iterator.
     * @since 2.9
     *//*
  public static <T> org.eclipse.emf.common.util.EList<T> toEList(Iterator<? extends T> iterator)
  {
    return ECollections.newBasicEList(iterator);
  }

  *//**
     * Returns a list containing the elements of the given iterable.
     * If the iterable is of type {@link org.eclipse.emf.common.util.EList}, that list itself is returned.
     * If the iterable is of type {@link List}, a {@link ECollections#asEList(List) view} of that list is returned;
     * all changes to view are reflected in the underlying list and all changes to the underlying list are reflected in the view.
     * In all other cases, the result is a {@link ECollections#newBasicEList(Iterable) copy} of the iterable.
     * @return a list containing the same elements as the given iterable.
     * @since 2.9
     *//*
  public static <T> org.eclipse.emf.common.util.EList<T> toEList(Iterable<? extends T> iterable)
  {
    if (iterable instanceof org.eclipse.emf.common.util.EList)
    {
      @SuppressWarnings("unchecked")
      org.eclipse.emf.common.util.EList<T> result = (org.eclipse.emf.common.util.EList<T>)iterable;
      return result;
    }
    else if (iterable instanceof List)
    {
      @SuppressWarnings("unchecked")
      final List<T> list = (List<T>)iterable;
      return
        new DelegatingEList<T>()
        {
          private static final long serialVersionUID = 1L;

          @Override
          protected List<T> delegateList()
          {
            return list;
          }
        };
    }
    else
    {
      return ECollections.newBasicEList(iterable);
    }
  }

  *//**
     * Returns a mutable EMap view of the specified map.
     * {@link org.eclipse.emf.common.util.EList#move(int, int)}, {@link org.eclipse.emf.common.util.EList#move(int, Object)}, {@link List#add(int, Object)}, and {@link List#addAll(int, Collection)},
     * i.e., the methods that expect to control the exact order of entries in the map's entry set,
     * are not supported and throw {@link UnsupportedOperationException}.
     * All other changes to the EMap write through to the underlying map.
     * @param map the map to which the EMap delegates.
     * @return an EMap view of the specified map.
     * @since 2.9
     *//*
  public static <K, V> EMap<K, V> asEMap(final Map<K, V> map)
  {
    return
      new EMap<K,V>()
      {
        public void move(int newPosition, Map.Entry<K, V> object)
        {
          throw new UnsupportedOperationException();
        }

        public Map.Entry<K, V> move(int newPosition, int oldPosition)
        {
          throw new UnsupportedOperationException();
        }

        public void add(int index, Map.Entry<K, V> element)
        {
          throw new UnsupportedOperationException();
        }

        public boolean addAll(int index, Collection<? extends Map.Entry<K, V>> c)
        {
          throw new UnsupportedOperationException();
        }

        public int size()
        {
          return map.size();
        }

        public boolean isEmpty()
        {
          return map.isEmpty();
        }

        public boolean contains(Object o)
        {
          return map.entrySet().contains(o) || map.containsKey(o);
        }

        public Iterator<Map.Entry<K, V>> iterator()
        {
          return map.entrySet().iterator();
        }

        public Object[] toArray()
        {
          return map.entrySet().toArray();
        }

        public <T> T[] toArray(T[] a)
        {
          return map.entrySet().toArray(a);
        }

        public boolean add(Map.Entry<K, V> e)
        {
          K key = e.getKey();
          boolean result = map.containsKey(key);
          map.put(key,  e.getValue());
          return result;
        }

        public boolean remove(Object o)
        {
          if (o instanceof Map.Entry)
          {
            Map.Entry<?, ?> entry = (Map.Entry<?, ?>)o;
            Object key = entry.getKey();
            if (map.containsKey(key))
            {
              map.remove(key);
              return true;
            }
          }
          if (map.containsKey(o))
          {
            map.remove(o);
            return true;
          }
          return false;
        }

        public boolean containsAll(Collection<?> c)
        {
          return map.entrySet().containsAll(c);
        }

        public boolean addAll(Collection<? extends Map.Entry<K, V>> c)
        {
          boolean result = false;
          for (Map.Entry<K, V> entry : c)
          {
            if (add(entry))
            {
              result = true;
            }
          }
          return result;
        }

        public boolean removeAll(Collection<?> c)
        {
          boolean result = false;
          for (Object o : c)
          {
            if (remove(o))
            {
              result = true;
            }
          }
          return result;
        }

        public boolean retainAll(Collection<?> c)
        {
          return listView().retainAll(c);
        }

        public void clear()
        {
          map.clear();
        }

        protected void rangeCheck(int index)
        {
          int size = map.size();
          if (index >= size || index < 0)
            throw new BasicIndexOutOfBoundsException(index, size);
        }

        public Map.Entry<K, V> get(int index)
        {
          rangeCheck(index);
          int i = 0;
          for (Map.Entry<K, V> entry : map.entrySet())
          {
            if (i++ == index)
            {
              return entry;
            }
          }
          return null;
        }

        public Map.Entry<K, V> set(int index, Map.Entry<K, V> element)
        {
          rangeCheck(index);
          int i = 0;
          for (Map.Entry<K, V> entry : map.entrySet())
          {
            if (i++ == index)
            {
              map.remove(entry.getKey());
              return entry;
            }
          }
          return null;
        }

        public Map.Entry<K, V> remove(int index)
        {
          rangeCheck(index);
          int i = 0;
          for (Map.Entry<K, V> entry : map.entrySet())
          {
            if (i++ == index)
            {
              map.remove(entry.getKey());
              return entry;
            }
          }
          return null;
        }

        public int indexOf(Object o)
        {
          int i = 0;
          for (Map.Entry<K, V> entry : map.entrySet())
          {
            if (entry.equals(o))
            {
              return i;
            }
          }
          return -1;
        }

        public int lastIndexOf(Object o)
        {
          return indexOf(o);
        }

        protected List<Map.Entry<K, V>> listView;

        protected Map.Entry<K, V> basicGet(int index)
        {
          return get(index);
        }

        protected List<Map.Entry<K, V>> listView()
        {
          if (listView == null)
          {
            listView =
              new AbstractList<Map.Entry<K, V>>()
              {
                @Override
                public Map.Entry<K, V> get(int index)
                {
                  return basicGet(index);
                }

                @Override
                public int size()
                {
                  return map.size();
                }
              };
          }
          return listView;
        }

        public ListIterator<Map.Entry<K, V>> listIterator()
        {
          return listView().listIterator();
        }

        public ListIterator<Map.Entry<K, V>> listIterator(int index)
        {
          return listView().listIterator(index);
        }

        public List<Map.Entry<K, V>> subList(int fromIndex, int toIndex)
        {
          return listView().subList(fromIndex, toIndex);
        }

        public V get(Object key)
        {
          return map.get(key);
        }

        public V put(K key, V value)
        {
          return map.put(key, value);
        }

        public void putAll(Map<? extends K, ? extends V> m)
        {
          map.putAll(m);
        }

        public void putAll(EMap<? extends K, ? extends V> m)
        {
          map.putAll(m.map());
        }

        public int indexOfKey(Object key)
        {
          int i = 0;
          for (Map.Entry<K, V> entry : map.entrySet())
          {
            if (key == null ? entry.getKey() == null : key.equals(entry.getKey()))
            {
              return i;
            }
          }
          return -1;
        }

        public boolean containsKey(Object key)
        {
          return map.containsKey(key);
        }

        public boolean containsValue(Object value)
        {
          return map.containsValue(value);
        }

        public V removeKey(Object key)
        {
          return map.remove(key);
        }

        public Map<K, V> map()
        {
          return map;
        }

        public Set<Map.Entry<K, V>> entrySet()
        {
          return map.entrySet();
        }

        public Set<K> keySet()
        {
          return map.keySet();
        }

        public Collection<V> values()
        {
          return map.values();
        }

        @Override
        public boolean equals(Object object)
        {
          if (object instanceof List<?>)
          {
            return listView().equals(object);
          }
          else
          {
            return false;
          }
        }

        @Override
        public int hashCode()
        {
          return listView().hashCode();
        }

        @Override
        public String toString()
        {
          return map.toString();
        }
      };
  }

  *//**
     * Returns an EList view of the specified list.
     * All changes to the EList write through to the underlying list.
     * @param list the list to which the EList delegates.
     * @return an EList view of the specified list.
     * @since 2.9
     *//*
  public static <T> org.eclipse.emf.common.util.EList<T> asEList(final List<T> list)
  {
    return
      new DelegatingEList<T>()
      {
        private static final long serialVersionUID = 1L;

        @Override
        protected List<T> delegateList()
        {
          return list;
        }
      };
  }*/

    /**
     * Returns a mutable, fixed-size, random access EList backed by the given array.
     * Changes to the list, i.e., set and move, write through to the array.
     * All other list modifying operations throw {@link UnsupportedOperationException}.
     * This is analogous to {@link Arrays#asList(Object...)} with the advantage that you can {@link org.eclipse.emf.common.util.EList#move(int, int) move} objects;
     * hence you can {@link #sort(org.eclipse.emf.common.util.EList) sort} without using {@link List#add(int, Object)} and {@link List#remove(int)}.
     * @param elements the array to which the EList delegates.
     * @return an EList view of the specified array.
     * @since 2.9
     */
    public static <T> org.eclipse.emf.common.util.EList<T> asEList(final T... elements) {
        return
                new BasicEList<T>() {
                    private static final long serialVersionUID = 1L;

                    {
                        this.data = elements;
                        size = elements.length;
                    }

                    @Override
                    public void setData(int size, Object[] data) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public void grow(int minimumCapacity) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public void shrink() {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public T remove(int index) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public boolean remove(Object object) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public boolean removeAll(Collection<?> collection) {
                        throw new UnsupportedOperationException();
                    }

                    @Override
                    public void clear() {
                        throw new UnsupportedOperationException();
                    }
                };
    }

    /**
     * Returns an unmodifiable view of the list.
     * @return an unmodifiable view of the list.
     * @since 2.9
     */
  /*public static <T> EList<T> unmodifiableEList(List<? extends T> list)
  {
    return new UnmodifiableEList<T>(list);
  }*/

    /**
     * Creates an empty mutable {@link BasicEList}.
     * @return an empty mutable {@link BasicEList}.
     * @since 2.9
     */
    public static <T> BasicEList<T> newBasicEList() {
        return new BasicEList<T>();
    }

    /**
     * Creates an empty mutable {@link BasicEList} with the given capacity.
     * @return an empty mutable {@link BasicEList}.
     * @since 2.9
     */
    public static <T> BasicEList<T> newBasicEListWithCapacity(int capacity) {
        return new BasicEList<T>(capacity);
    }

    /**
     * Creates an empty mutable {@link BasicEList} with a capacity large enough to hold a bit more than the estimated number of elements.
     * If you know the exact size, use {@link #newBasicEListWithCapacity(int)} instead.
     * @return an empty mutable {@link BasicEList}.
     * @since 2.9
     */
    public static <T> BasicEList<T> newBasicEListWithExpectedSize(int estimatedSize) {
        BasicEList<T> result = new BasicEList<T>();
        result.grow(estimatedSize);
        return result;
    }

    /**
     * Creates a mutable {@link BasicEList} containing the given elements.
     * @return a mutable {@link BasicEList} containing the given elements.
     * @since 2.9
     */
    public static <T> BasicEList<T> newBasicEList(T... elements) {
        BasicEList<T> result = new BasicEList<T>(elements.length);
        for (T t : elements) {
            result.add(t);
        }
        return result;
    }

    /**
     * Creates a mutable {@link BasicEList} containing the given elements.
     * @return a mutable {@link BasicEList} containing the given elements.
     * @since 2.9
     */
    public static <T> BasicEList<T> newBasicEList(Iterator<? extends T> iterator) {
        BasicEList<T> result = new BasicEList<T>();
        while (iterator.hasNext()) {
            result.add(iterator.next());
        }
        return result;
    }

    /**
     * Creates a mutable {@link BasicEList} containing the given elements.
     * @return a mutable {@link BasicEList} containing the given elements.
     * @since 2.9
     */
    public static <T> BasicEList<T> newBasicEList(Iterable<? extends T> iterable) {
        if (iterable instanceof Collection) {
            return new BasicEList<T>((Collection<? extends T>) iterable);
        } else {
            BasicEList<T> result = new BasicEList<T>();
            for (T t : iterable) {
                result.add(t);
            }
            return result;
        }
    }
}
